#include "ObjModel.h"
#include "utils.h"
#include <sstream>
#include <iostream>
#include <string>
#include <cstring>
#include <vector>
using namespace std;

void ObjModel::Init(const char * modelFilePath)
{
	struct VertexInfo 
	{
		float v[3] = { 0 };
	};

	struct VertexDefine
	{
		int positionIndex;
		int texcoordIndex;
		int normalIndex;
	};
	vector<VertexInfo> positions;
	vector<VertexInfo> texcoords;
	vector<VertexInfo> normals;
	vector<VertexDefine> vertices;
	vector<unsigned int> faces;
	//load model from file
	char * fileContent = LoadFileContent(modelFilePath);
	stringstream ssFileContent(fileContent);
	char szOneLine[256];
	string temp;
	//decode model
	while (!ssFileContent.eof())
	{
		memset(szOneLine, 0, sizeof(szOneLine));
		ssFileContent.getline(szOneLine, 256);
		if (strlen(szOneLine) <= 0)
		{
			continue;
		}
		stringstream ssOneLine(szOneLine);
		if (szOneLine[0] == 'v')
		{
			VertexInfo vi;
			if (szOneLine[1] == 't')
			{
				ssOneLine >> temp;
				ssOneLine >> vi.v[0];
				ssOneLine >> vi.v[1];
				texcoords.push_back(vi);
				//cout << "texcoord: " << vi.v[0] << "," << vi.v[1] << endl;
			}
			else if (szOneLine[1] == 'n')
			{
				ssOneLine >> temp;
				ssOneLine >> vi.v[0];
				ssOneLine >> vi.v[1];
				ssOneLine >> vi.v[2];
				normals.push_back(vi);
				//cout << "normal: " << vi.v[0] << "," << vi.v[1] << "," << vi.v[2] << endl;
			}
			else
			{
				ssOneLine >> temp;
				ssOneLine >> vi.v[0];
				ssOneLine >> vi.v[1];
				ssOneLine >> vi.v[2];
				positions.push_back(vi);
				//cout << "position: " << vi.v[0] << "," << vi.v[1] << "," << vi.v[2] << endl;;
			}
		}
		else if (szOneLine[0] == 'f')
		{
			ssOneLine >> temp;
			string vertexStr;
			for (int i = 0; i < 3; ++i)
			{
				ssOneLine >> vertexStr;
				size_t pos = vertexStr.find_first_of('/');
				string positionIndexStr = vertexStr.substr(0, pos);
				size_t pos2 = vertexStr.find_first_of('/', pos + 1);
				string texcoordIndexStr = vertexStr.substr(pos + 1, pos2 - pos - 1);
				string normalIndexStr = vertexStr.substr(pos2 + 1, vertexStr.length() - pos2 - 1);
				VertexDefine vd;
				vd.positionIndex = atoi(positionIndexStr.c_str()) - 1;
				vd.texcoordIndex = atoi(texcoordIndexStr.c_str()) - 1;
				vd.normalIndex = atoi(normalIndexStr.c_str()) - 1;
				//trim the same vertice
				int nCurrentVertexIndex = -1;
				size_t nCurrentVerticeCount = vertices.size();
				for (int j = 0; j < nCurrentVerticeCount; ++j)
				{
					if (vertices[j].positionIndex == vd.positionIndex &&
						vertices[j].texcoordIndex == vd.texcoordIndex &&
						vertices[j].normalIndex == vd.normalIndex)
					{
						nCurrentVertexIndex = j;
						break;
					}
				}
				if (nCurrentVertexIndex == -1)
				{
					nCurrentVertexIndex = (int)vertices.size();
					vertices.push_back(vd);
				}
				faces.push_back(nCurrentVertexIndex);
			}
		}
	}
	//cout << "face size: " << faces.size() / 3 << endl;
	//covert ti opengl ibo & vbo
	int vertexCount = vertices.size();
	VertexData * vertexes = new VertexData[vertexCount];
	for (int i = 0; i < vertexCount; ++i)
	{
		memcpy(vertexes[i].position, positions[vertices[i].positionIndex].v, sizeof(float) * 3);
		memcpy(vertexes[i].texcoord, texcoords[vertices[i].texcoordIndex].v, sizeof(float) * 2);
		memcpy(vertexes[i].normal, normals[vertices[i].normalIndex].v, sizeof(float) * 3);
	}
	//create vbo
	glGenBuffers(1, &mVBO);
	glBindBuffer(GL_ARRAY_BUFFER, mVBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(VertexData) * vertexCount, vertexes, GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	delete[] vertexes;

	//create ibo
	mIndexCount = faces.size();
	unsigned int * indexes = new unsigned int[mIndexCount];
	for (int i = 0; i < mIndexCount; ++i)
	{
		indexes[i] = faces[i];
	}
	glGenBuffers(1, &mIBO);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * mIndexCount, indexes, GL_STATIC_DRAW);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	delete[] indexes;
	delete fileContent;
}

void ObjModel::Bind(GLint posLoc, GLint texcoordLoc, GLint normalLoc)
{
	glBindBuffer(GL_ARRAY_BUFFER, mVBO);
	glEnableVertexAttribArray(posLoc);
	glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, sizeof(VertexData), 0);

	glEnableVertexAttribArray(texcoordLoc);
	glVertexAttribPointer(texcoordLoc, 2, GL_FLOAT, GL_FALSE, sizeof(VertexData), (void*)(sizeof(float) * 3));

	glEnableVertexAttribArray(normalLoc);
	glVertexAttribPointer(normalLoc, 4, GL_FLOAT, GL_FALSE, sizeof(VertexData), (void*)(sizeof(float) * 5));
	glBindBuffer(GL_ARRAY_BUFFER, 0);
}

void ObjModel::Draw()
{
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIBO);
	glDrawElements(GL_TRIANGLES, mIndexCount, GL_UNSIGNED_INT, 0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
